﻿using System;
using System.Linq;
using Dexter.DataTransferObject;
using Dexter.Domain.Model;
using Dexter.Search.Lucene.Configuration;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Util;

namespace Dexter.Search.Lucene.DocumentDefinition {
	internal class ItemDefinition :IResultDefinition<SearchItem> {
		readonly ILuceneConfiguration config;

		public ItemDefinition ( ILuceneConfiguration config ) {
			this.config = config;
		}

		public Document Convert ( Item entity ) {
			Document doc = new Document ( );

			Field idField = new Field (
				FieldsName.Item.ID,
				NumericUtils.IntToPrefixCoded ( entity.Id ),
				Field.Store.YES,
				Field.Index.NOT_ANALYZED,
				Field.TermVector.NO );

			Field titleField = new Field (
				FieldsName.Item.TITLE,
				entity.Title,
				Field.Store.YES,
				Field.Index.ANALYZED,
				Field.TermVector.YES );
			titleField.SetBoost ( config.TitleBoost );

			Field slugField = new Field (
				FieldsName.Item.SLUG,
				entity.Slug,
				Field.Store.YES,
				Field.Index.NO,
				Field.TermVector.NO );

			Field abstractField = new Field (
				FieldsName.Item.ABSTRACT,
				entity.Abstract,
				Field.Store.YES,
				Field.Index.NO,
				Field.TermVector.NO );

			Field breakOnAggregateField = new Field (
				FieldsName.Item.BREAK_ON_AGGREGATE,
				entity.BreakOnAggregate.ToString ( ),
				Field.Store.YES,
				Field.Index.NO,
				Field.TermVector.NO );

			Field formattedBodyField = new Field (
				FieldsName.Item.FORMATTED_BODY,
				entity.FormattedBody ?? string.Empty,
				Field.Store.YES,
				Field.Index.ANALYZED,
				Field.TermVector.YES );

			Field bodyField = new Field (
				FieldsName.Item.BODY,
				entity.FormattedBody == null
					? string.Empty
					: entity.FormattedBody.CleanHtmlText ( ),
				Field.Store.YES,
				Field.Index.ANALYZED,
				Field.TermVector.YES );
			bodyField.SetBoost ( config.BodyBoost );

			Field publishDateField = new Field (
				FieldsName.Item.PUBLISH_DATE,
				DateTools.DateToString ( entity.PublishDate, DateTools.Resolution.MINUTE ),
				Field.Store.YES,
				Field.Index.NOT_ANALYZED,
				Field.TermVector.NO );

			Field usernameField = new Field (
				FieldsName.Item.USERNAME,
				entity.Username,
				Field.Store.YES,
				Field.Index.NO,
				Field.TermVector.NO );

			Field tagsField = new Field (
				FieldsName.Item.TAGS,
				entity.GetCommaSeparatedTags ( ),
				Field.Store.YES,
				Field.Index.ANALYZED,
				Field.TermVector.YES );
			tagsField.SetBoost ( config.TagsBoost );

			string categories = null;

			if (entity.GetType ( ) == typeof ( Post )) {
				categories = string.Join ( ",", ((Post)entity).FlatCategories.Select ( x => x.Name ).ToArray ( ) );
			}

			Field categoriesField = new Field (
				FieldsName.Item.CATEGORIES,
				categories,
				Field.Store.YES,
				Field.Index.NO,
				Field.TermVector.NO );

			Field commentEnabledField = new Field (
				FieldsName.Item.COMMENT_ENABLED,
				entity.CommentEnabled.ToString ( ),
				Field.Store.YES,
				Field.Index.NO,
				Field.TermVector.NO );

			Field totalCommentsField = new Field (
				FieldsName.Item.TOTAL_COMMENTS,
				NumericUtils.IntToPrefixCoded ( entity.TotalComments ),
				Field.Store.YES,
				Field.Index.NO,
				Field.TermVector.NO );

			Field totalTrackbackField = new Field (
				FieldsName.Item.TOTAL_TRACKBACK,
				NumericUtils.IntToPrefixCoded ( entity.TotalTrackback ),
				Field.Store.YES,
				Field.Index.NO,
				Field.TermVector.NO );

			Field publishField = new Field (
				FieldsName.Item.PUBLISH,
				entity.IsPublished.ToString ( ),
				Field.Store.YES,
				Field.Index.NOT_ANALYZED,
				Field.TermVector.NO );

			Field typeField = new Field (
				FieldsName.INDEX_FIELD_TYPE,
				entity.GetType ( ).FullName,
				Field.Store.YES,
				Field.Index.NO,
				Field.TermVector.NO );


			doc.Add ( idField );
			doc.Add ( titleField );
			doc.Add ( slugField );
			doc.Add ( abstractField );
			doc.Add ( breakOnAggregateField );
			doc.Add ( formattedBodyField );
			doc.Add ( bodyField );
			doc.Add ( publishDateField );
			doc.Add ( usernameField );
			doc.Add ( tagsField );
			doc.Add ( categoriesField );
			doc.Add ( commentEnabledField );
			doc.Add ( totalCommentsField );
			doc.Add ( totalTrackbackField );
			doc.Add ( publishField );
			doc.Add ( typeField );

			return doc;
		}

		public Term GetIndex ( Item entity ) {
			return new Term ( FieldsName.Item.ID, entity.Id.ToString ( ) );
		}

		public SearchItem Convert ( Document document ) {
			SearchItem result = new SearchItem ( );
			result.Id = NumericUtils.PrefixCodedToInt ( document.Get ( FieldsName.Item.ID ) );
			result.Title = document.Get ( FieldsName.Item.TITLE );
			result.Abstract = document.Get ( FieldsName.Item.ABSTRACT );
			var categories = document.Get ( FieldsName.Item.CATEGORIES );

			result.Categories = string.IsNullOrEmpty ( categories )
									? null
									: categories.Split ( new[] { ',' }, StringSplitOptions.None );

			result.FormattedBody = document.Get ( FieldsName.Item.FORMATTED_BODY );
			result.Body = document.Get ( FieldsName.Item.BODY );
			result.Publish = document.Get ( FieldsName.Item.PUBLISH ).ToBoolean ( );
			result.PublishDate = DateTools.StringToDate ( document.Get ( FieldsName.Item.PUBLISH_DATE ) );

			result.Slug = document.Get ( FieldsName.Item.SLUG );

			var tags = document.Get ( FieldsName.Item.TAGS );

			result.Tags = string.IsNullOrEmpty ( tags )
									? null
									: tags.Split ( new[] { ',' }, StringSplitOptions.None );

			result.TotalComments = NumericUtils.PrefixCodedToInt ( document.Get ( FieldsName.Item.TOTAL_COMMENTS ) );
			result.TotalTrackback = NumericUtils.PrefixCodedToInt ( document.Get ( FieldsName.Item.TOTAL_TRACKBACK ) );
			result.Username = document.Get ( FieldsName.Item.USERNAME );

			return result;
		}
	}
}
